<template>
  <div class="video-frame-extractor">
    <div class="header">
      <h1>视频截帧工具</h1>
      <p>上传视频并提取任意时间点的帧图像</p>
    </div>

    <div class="app-card">
      <div class="app-body">
        <!-- 视频上传和预览区域 -->
        <div class="video-section">
          <div class="section-title">
            <svg
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M23 7L16 12L23 17V7Z"
                stroke="#3498db"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
              <rect
                x="1"
                y="5"
                width="15"
                height="14"
                rx="2"
                stroke="#3498db"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
            </svg>
            <span>视频预览</span>
          </div>

          <div
            class="upload-area"
            v-if="!videoSrc"
            @click="triggerFileInput"
            @drop="handleDrop"
            @dragover.prevent
          >
            <input
              type="file"
              ref="fileInput"
              @change="handleFileSelect"
              accept="video/*"
              style="display: none"
            />
            <div class="upload-content">
              <svg
                width="64"
                height="64"
                viewBox="0 0 24 24"
                fill="none"
                xmlns="http://www.w3.org/2000/svg"
              >
                <path
                  d="M14 2H6C5.46957 2 4.96086 2.21071 4.58579 2.58579C4.21071 2.96086 4 3.46957 4 4V20C4 20.5304 4.21071 21.0391 4.58579 21.4142C4.96086 21.7893 5.46957 22 6 22H18C18.5304 22 19.0391 21.7893 19.4142 21.4142C19.7893 21.0391 20 20.5304 20 20V8L14 2Z"
                  stroke="#3498db"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M14 2V8H20"
                  stroke="#3498db"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M16 13H8"
                  stroke="#3498db"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M16 17H8"
                  stroke="#3498db"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
                <path
                  d="M10 9H9H8"
                  stroke="#3498db"
                  stroke-width="2"
                  stroke-linecap="round"
                  stroke-linejoin="round"
                />
              </svg>
              <h3>点击或拖拽视频文件到此处</h3>
              <p>支持 MP4, WebM, OGG 等常见视频格式</p>
            </div>
          </div>

          <div v-else class="video-container">
            <video
              ref="videoPlayer"
              :src="videoSrc"
              controls
              @loadedmetadata="onVideoLoaded"
              @timeupdate="onTimeUpdate"
              class="video-player"
            ></video>

            <div class="video-info">
              <div class="video-details">
                <span class="video-name">{{ videoName }}</span>
                <span class="video-duration">{{ formattedDuration }}</span>
              </div>

              <button @click="removeVideo" class="btn btn-secondary">
                <svg
                  width="16"
                  height="16"
                  viewBox="0 0 24 24"
                  fill="none"
                  xmlns="http://www.w3.org/2000/svg"
                >
                  <path
                    d="M3 6H5H21"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M8 6V4C8 3.46957 8.21071 2.96086 8.58579 2.58579C8.96086 2.21071 9.46957 2 10 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4V6M19 6V20C19 20.5304 18.7893 21.0391 18.4142 21.4142C18.0391 21.7893 17.5304 21 17 21H7C6.46957 21 5.96086 21.7893 5.58579 21.4142C5.21071 21.0391 5 20.5304 5 20V6H19Z"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M10 11V17"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                  <path
                    d="M14 11V17"
                    stroke="currentColor"
                    stroke-width="2"
                    stroke-linecap="round"
                    stroke-linejoin="round"
                  />
                </svg>
                移除视频
              </button>
            </div>
          </div>
        </div>

        <!-- 控制面板和帧预览区域 -->
        <div class="frames-section">
          <div class="section-title">
            <svg
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <rect
                x="3"
                y="3"
                width="18"
                height="18"
                rx="2"
                stroke="#3498db"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
              <path
                d="M3 9H21"
                stroke="#3498db"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
            </svg>
            <span>帧提取</span>
            <span class="frame-count" v-if="frames.length > 0">({{ frames.length }} 帧)</span>
          </div>

          <div v-if="videoSrc" class="controls-panel">
            <div class="time-controls">
              <div class="time-display">
                <span>当前时间: {{ formatTime(currentTime) }}</span>
                <span>总时长: {{ formatTime(videoDuration) }}</span>
              </div>

              <div class="slider-container">
                <input
                  type="range"
                  v-model="currentTime"
                  :max="videoDuration"
                  step="0.1"
                  class="time-slider"
                  @input="seekVideo"
                />
              </div>
            </div>

            <div class="extraction-controls">
              <div class="time-input">
                <label for="frameTime">提取时间点 (秒):</label>
                <input
                  id="frameTime"
                  type="number"
                  v-model.number="frameTime"
                  :min="0"
                  :max="videoDuration"
                  step="0.1"
                />
              </div>

              <div class="action-buttons">
                <button @click="captureFrame" class="btn btn-primary" :disabled="!videoLoaded">
                  <svg
                    width="16"
                    height="16"
                    viewBox="0 0 24 24"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <rect
                      x="3"
                      y="3"
                      width="18"
                      height="18"
                      rx="2"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <circle
                      cx="12"
                      cy="12"
                      r="3"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                  截取当前帧
                </button>

                <button
                  @click="captureMultipleFrames"
                  class="btn btn-secondary"
                  :disabled="!videoLoaded"
                >
                  <svg
                    width="16"
                    height="16"
                    viewBox="0 0 24 24"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      d="M4 15C4 12.7909 5.79086 11 8 11H16C18.2091 11 20 12.7909 20 15V19C20 21.2091 18.2091 23 16 23H8C5.79086 23 4 21.2091 4 19V15Z"
                      stroke="currentColor"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M12 11V7C12 5.89543 11.1046 5 10 5H9C7.89543 5 7 4.10457 7 3V3C7 1.89543 7.89543 1 9 1H15C16.1046 1 17 1.89543 17 3V3C17 4.10457 16.1046 5 15 5H14C12.8954 5 12 5.89543 12 7V11Z"
                      stroke="currentColor"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                  批量截取
                </button>

                <button @click="clearFrames" class="btn btn-danger" :disabled="frames.length === 0">
                  <svg
                    width="16"
                    height="16"
                    viewBox="0 0 24 24"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      d="M3 6H5H21"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M8 6V4C8 3.46957 8.21071 2.96086 8.58579 2.58579C8.96086 2.21071 9.46957 2 10 2H14C14.5304 2 15.0391 2.21071 15.4142 2.58579C15.7893 2.96086 16 3.46957 16 4V6M19 6V20C19 20.5304 18.7893 21.0391 18.4142 21.4142C18.0391 21.7893 17.5304 21 17 21H7C6.46957 21 5.96086 21.7893 5.58579 21.4142C5.21071 21.0391 5 20.5304 5 20V6H19Z"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M10 11V17"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M14 11V17"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                  清空所有帧
                </button>
              </div>
            </div>
          </div>

          <div v-if="frames.length > 0" class="frames-grid">
            <div
              v-for="(frame, index) in frames"
              :key="index"
              class="frame-item"
              @click="downloadFrame(frame)"
            >
              <img :src="frame.dataUrl" :alt="`帧 ${index + 1}`" />
              <div class="frame-info">
                <span class="frame-time">{{ formatTime(frame.time) }}</span>
                <button @click.stop="downloadFrame(frame)" class="btn-download">
                  <svg
                    width="14"
                    height="14"
                    viewBox="0 0 24 24"
                    fill="none"
                    xmlns="http://www.w3.org/2000/svg"
                  >
                    <path
                      d="M21 15V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V15"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M7 10L12 15L17 10"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                    <path
                      d="M12 15V3"
                      stroke="white"
                      stroke-width="2"
                      stroke-linecap="round"
                      stroke-linejoin="round"
                    />
                  </svg>
                </button>
              </div>
            </div>
          </div>

          <div v-else-if="videoSrc" class="empty-state">
            <svg
              width="64"
              height="64"
              viewBox="0 0 24 24"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <rect
                x="3"
                y="3"
                width="18"
                height="18"
                rx="2"
                stroke="#bdc3c7"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
              <circle
                cx="12"
                cy="12"
                r="3"
                stroke="#bdc3c7"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
            </svg>
            <p>截取的帧将在这里显示</p>
            <p class="hint">点击时间轴上的位置，然后点击"截取当前帧"</p>
          </div>

          <div v-else class="empty-state">
            <svg
              width="64"
              height="64"
              viewBox="0 0 24 24"
              fill="none"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="M23 7L16 12L23 17V7Z"
                stroke="#bdc3c7"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
              <rect
                x="1"
                y="5"
                width="15"
                height="14"
                rx="2"
                stroke="#bdc3c7"
                stroke-width="2"
                stroke-linecap="round"
                stroke-linejoin="round"
              />
            </svg>
            <p>请先上传视频文件</p>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { ref, computed, onMounted, onUnmounted } from 'vue'

export default {
  name: 'VideoFrameExtractor',
  setup () {
    // 响应式数据
    const videoSrc = ref('')
    const videoName = ref('')
    const videoPlayer = ref(null)
    const fileInput = ref(null)
    const videoLoaded = ref(false)
    const videoDuration = ref(0)
    const currentTime = ref(0)
    const frameTime = ref(0)
    const frames = ref([])
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')

    // 计算属性
    const formattedDuration = computed(() => {
      return formatTime(videoDuration.value)
    })

    // 方法
    const triggerFileInput = () => {
      fileInput.value.click()
    }

    const handleFileSelect = (event) => {
      const file = event.target.files[0]
      if (file && file.type.startsWith('video/')) {
        videoSrc.value = URL.createObjectURL(file)
        videoName.value = file.name
      }
    }

    const handleDrop = (event) => {
      event.preventDefault()
      const file = event.dataTransfer.files[0]
      if (file && file.type.startsWith('video/')) {
        videoSrc.value = URL.createObjectURL(file)
        videoName.value = file.name
      }
    }

    const removeVideo = () => {
      videoSrc.value = ''
      videoName.value = ''
      videoLoaded.value = false
      videoDuration.value = 0
      currentTime.value = 0
      frames.value = []
      if (videoPlayer.value) {
        videoPlayer.value.pause()
      }
    }

    const onVideoLoaded = () => {
      videoLoaded.value = true
      console.log(videoPlayer.value.duration)
      videoDuration.value = videoPlayer.value.duration
      frameTime.value = 0
    }

    const onTimeUpdate = () => {
      currentTime.value = videoPlayer.value.currentTime
    }

    const seekVideo = () => {
      if (videoPlayer.value) {
        console.log(currentTime.value)
        videoPlayer.value.currentTime = currentTime.value
        frameTime.value = currentTime.value
      }
    }

    const formatTime = (time) => {
      if (isNaN(time)) return '00:00'

      const minutes = Math.floor(time / 60)
      const seconds = Math.floor(time % 60)
      return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`
    }

    const captureFrame = () => {
      if (!videoPlayer.value || !videoLoaded.value) return

      // 保存当前时间点
      const originalTime = videoPlayer.value.currentTime

      // 设置到指定时间
      videoPlayer.value.currentTime = frameTime.value

      // 等待视频seek完成
      videoPlayer.value.onseeked = () => {
        // 设置canvas尺寸与视频相同
        canvas.width = videoPlayer.value.videoWidth
        canvas.height = videoPlayer.value.videoHeight

        // 绘制当前帧到canvas
        ctx.drawImage(videoPlayer.value, 0, 0, canvas.width, canvas.height)

        // 获取数据URL
        const dataUrl = canvas.toDataURL('image/png')

        // 添加到帧列表
        frames.value.push({
          time: frameTime.value,
          dataUrl: dataUrl
        })

        // 恢复原始时间
        videoPlayer.value.currentTime = originalTime
        videoPlayer.value.onseeked = null
      }
    }

    const captureMultipleFrames = () => {
      if (!videoPlayer.value || !videoLoaded.value) return

      // 保存当前时间点
      const originalTime = videoPlayer.value.currentTime

      // 计算间隔（例如每10秒截取一帧）
      const interval = 10
      const frameCount = Math.floor(videoDuration.value / interval)

      // 清空现有帧
      frames.value = []

      // 批量截取帧
      let captured = 0

      const captureNextFrame = () => {
        if (captured >= frameCount) {
          // 恢复原始时间
          videoPlayer.value.currentTime = originalTime
          return
        }

        const time = captured * interval
        videoPlayer.value.currentTime = time

        videoPlayer.value.onseeked = () => {
          // 设置canvas尺寸与视频相同
          canvas.width = videoPlayer.value.videoWidth
          canvas.height = videoPlayer.value.videoHeight

          // 绘制当前帧到canvas
          ctx.drawImage(videoPlayer.value, 0, 0, canvas.width, canvas.height)

          // 获取数据URL
          const dataUrl = canvas.toDataURL('image/png')

          // 添加到帧列表
          frames.value.push({
            time: time,
            dataUrl: dataUrl
          })

          captured++
          captureNextFrame()
        }
      }

      captureNextFrame()
    }

    const clearFrames = () => {
      frames.value = []
    }

    const downloadFrame = (frame) => {
      const link = document.createElement('a')
      link.download = `frame_${formatTime(frame.time).replace(/:/g, '-')}.png`
      link.href = frame.dataUrl
      link.click()
    }

    // 生命周期
    onMounted(() => {
      // 添加拖放事件监听器
      document.addEventListener('dragover', (e) => e.preventDefault())
      document.addEventListener('drop', (e) => e.preventDefault())
    })

    onUnmounted(() => {
      // 清理URL对象
      if (videoSrc.value) {
        URL.revokeObjectURL(videoSrc.value)
      }
    })

    return {
      videoSrc,
      videoName,
      videoPlayer,
      fileInput,
      videoLoaded,
      videoDuration,
      currentTime,
      frameTime,
      frames,
      formattedDuration,
      triggerFileInput,
      handleFileSelect,
      handleDrop,
      removeVideo,
      onVideoLoaded,
      onTimeUpdate,
      seekVideo,
      formatTime,
      captureFrame,
      captureMultipleFrames,
      clearFrames,
      downloadFrame
    }
  }
}
</script>

<style scoped>
.video-frame-extractor {
  min-height: 100vh;
  background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
  color: #333;
  padding: 20px;
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}

.header {
  text-align: center;
  margin-bottom: 30px;
  color: white;
}

.header h1 {
  font-size: 1rem;
  margin-bottom: 10px;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
}

.header p {
  font-size: 0.5rem;
  opacity: 0.9;
}

.app-card {
  background: white;
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  max-width: 1200px;
  margin: 0 auto;
}

.app-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 30px;
}

@media (min-width: 768px) {
  .app-body {
    flex-direction: row;
  }
}

.video-section,
.frames-section {
  flex: 1;
}

.section-title {
  font-size: 0.6rem;
  margin-bottom: 15px;
  color: #2c3e50;
  display: flex;
  align-items: center;
  gap: 10px;
}

.frame-count {
  font-size: 0.4rem;
  color: #7f8c8d;
  font-weight: normal;
}

.upload-area {
  border: 2px dashed #3498db;
  border-radius: 8px;
  padding: 40px 20px;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s;
  background-color: #f8fafc;
}

.upload-area:hover {
  background-color: #e8f4fd;
  border-color: #2980b9;
}

.upload-content h3 {
  margin: 15px 0 10px;
  color: #3498db;
}

.upload-content p {
  color: #7f8c8d;
  margin: 0;
}

.video-container {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.video-player {
  width: 100%;
  border-radius: 8px;
  background: #000;
}

.video-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.video-details {
  display: flex;
  flex-direction: column;
}

.video-name {
  font-weight: 600;
  color: #2c3e50;
}

.video-duration {
  font-size: 0.4rem;
  color: #7f8c8d;
}

.controls-panel {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-bottom: 20px;
}

.time-controls {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.time-display {
  display: flex;
  justify-content: space-between;
  font-size: 0.4rem;
  color: #7f8c8d;
}

.slider-container {
  width: 100%;
}

.time-slider {
  width: 100%;
  height: 6px;
  border-radius: 3px;
  background: #ddd;
  outline: none;
  -webkit-appearance: none;
}

.time-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #3498db;
  cursor: pointer;
}

.time-slider::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #3498db;
  cursor: pointer;
  border: none;
}

.extraction-controls {
  display: flex;
  flex-direction: column;
  gap: 15px;
}

.time-input {
  display: flex;
  align-items: center;
  gap: 10px;
}

.time-input label {
  font-weight: 600;
  white-space: nowrap;
}

.time-input input {
  padding: 8px 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  width: 100px;
}

.action-buttons {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
}

.btn {
  padding: 10px 16px;
  border: none;
  border-radius: 6px;
  font-weight: 600;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: all 0.3s;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-primary {
  background-color: #3498db;
  color: white;
}

.btn-primary:hover:not(:disabled) {
  background-color: #2980b9;
  transform: translateY(-2px);
}

.btn-secondary {
  background-color: #ecf0f1;
  color: #2c3e50;
}

.btn-secondary:hover:not(:disabled) {
  background-color: #bdc3c7;
}

.btn-danger {
  background-color: #e74c3c;
  color: white;
}

.btn-danger:hover:not(:disabled) {
  background-color: #c0392b;
  transform: translateY(-2px);
}

.frames-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 15px;
  max-height: 400px;
  overflow-y: auto;
  padding: 10px;
  border: 1px solid #eee;
  border-radius: 8px;
}

.frame-item {
  border: 1px solid #ddd;
  border-radius: 6px;
  overflow: hidden;
  cursor: pointer;
  transition: all 0.3s;
}

.frame-item:hover {
  transform: translateY(-5px);
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}

.frame-item img {
  width: 100%;
  height: 100px;
  object-fit: cover;
  display: block;
}

.frame-info {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  background: #f8f9fa;
}

.frame-time {
  font-size: 0.3rem;
  color: #7f8c8d;
}

.btn-download {
  background: #3498db;
  border: none;
  border-radius: 4px;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 300px;
  color: #7f8c8d;
  text-align: center;
  border: 2px dashed #ddd;
  border-radius: 8px;
}

.empty-state p {
  margin-top: 15px;
  font-size: 0.2rem;
}

.hint {
  font-size: 0.2rem !important;
  margin-top: 5px !important;
}

@media (max-width: 768px) {
  .header h1 {
    font-size: 1rem;
  }

  .app-body {
    padding: 20px;
  }

  .video-info {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .extraction-controls {
    gap: 10px;
  }

  .action-buttons {
    flex-direction: column;
  }

  .frames-grid {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  }
}
</style>